package com.xd.common.ascept;


import com.alibaba.fastjson.JSONObject;
import com.google.gson.*;
import com.xd.common.exception.XkdException;
import com.xd.common.util.RSAUtils;
import com.xd.common.util.XkdUtil;
import com.xd.core.system.dto.LoginVo;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;


@Slf4j
@Aspect
@Component
public class CheckSignAspect {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Value("${h5rsakey.privateKey}")
    private String privateKey;

    @Pointcut("@annotation(CheckSign)")
    public void checkSignAroud() {

    }

    @Around(value = "checkSignAroud()")
    public Object checkSign(ProceedingJoinPoint joinPoint) throws Throwable {
        Map<String, Object> stringObjectMap = new HashMap<>();
        String nonce = null;
        String sign = null;
        Boolean b = null;

        stringObjectMap = extractMethodParams(joinPoint);
        nonce = stringObjectMap.get("nonce").toString();
        sign = stringObjectMap.get("sign").toString();
        b = XkdUtil.checkSign(stringObjectMap, sign);

        Object o = redisTemplate.opsForValue().get("nonce:" + nonce);
        if (o != null)
            throw new XkdException("重复请求");
        if (Boolean.FALSE.equals(b))
            throw new XkdException("签名验证失败");
        //5分钟后失效
        redisTemplate.opsForValue().set("nonce:" + nonce, "1", 5 * 60, TimeUnit.SECONDS);
        return joinPoint.proceed();
    }

    public Map<String, Object> extractMethodParams(JoinPoint joinPoint) {
        Map<String, Object> paramMap = new HashMap<>();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        String[] paramNames = methodSignature.getParameterNames();
        Object[] args = joinPoint.getArgs();
        Parameter[] parameters = method.getParameters();

        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            Object value = args[i];
            String name = paramNames[i];

            // 忽略 HttpServletRequest、HttpServletResponse 等 Servlet 类型参数
            if (value instanceof ServletRequest || value instanceof ServletResponse) {
                continue;
            }

            // 处理 @RequestBody 参数：转换为 Map
            if (parameter.isAnnotationPresent(RequestBody.class)) {
                if (value != null) {
                    Map<String, Object> bodyMap = convertObjectToMap(value);
                    paramMap.putAll(bodyMap); // 将 body 参数展开放入 paramMap 中
                }
            }
            // 处理 @RequestParam、@PathVariable 或者没有注解但是基本类型
            else {
                paramMap.put(name, value);
            }
        }

        return paramMap;
    }

    //    private Map<String, Object> convertObjectToMap(Object obj) {
//        Map<String, Object> result = new HashMap<>();
//        if (obj == null) return result;
//        Field[] fields = obj.getClass().getDeclaredFields();
//        for (Field field : fields) {
//            field.setAccessible(true);
//            try {
//                result.put(field.getName(), field.get(obj));
//            } catch (IllegalAccessException e) {
//                // log error
//            }
//        }
//        return result;
//    }
    private Map<String, Object> convertObjectToMap(Object obj) {
        Map<String, Object> result = new HashMap<>();
        if (obj == null) return result;

        Class<?> currentClass = obj.getClass();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        while (currentClass != null && currentClass != Object.class) {
            Field[] fields = currentClass.getDeclaredFields();
            for (Field field : fields) {
                try {
                    String name = field.getName();
                    boolean accessFlag = field.isAccessible();
                    field.setAccessible(true);
                    Object value = field.get(obj);
                    if (value instanceof Collection) {
//                        Gson gson = new Gson();
                        Gson gson = new GsonBuilder()
                                .disableHtmlEscaping().setDateFormat("yyyy-MM-dd")  // 👈 禁用 HTML 转义
                                .create();

                        JsonElement jsonElement = gson.toJsonTree(value);
                        JsonElement sortedElement = sortJsonElement(jsonElement);
                        value = gson.toJson(sortedElement);
                    }

                    if (value instanceof Date) {
                        value = sdf.format((Date) value);
                    }
                    field.setAccessible(accessFlag);
                    result.put(name, value);
                } catch (IllegalAccessException e) {
                    // 可以记录日志
                    e.printStackTrace();
                }
            }
            currentClass = currentClass.getSuperclass(); // 向上遍历父类
        }

        return result;
    }

    private static JsonElement sortJsonElement(JsonElement element) {
        if (element.isJsonObject()) {
            JsonObject sorted = new JsonObject();
            TreeMap<String, JsonElement> map = new TreeMap<>();

            for (Map.Entry<String, JsonElement> entry : element.getAsJsonObject().entrySet()) {
                map.put(entry.getKey(), sortJsonElement(entry.getValue()));
            }

            for (Map.Entry<String, JsonElement> entry : map.entrySet()) {
                sorted.add(entry.getKey(), entry.getValue());
            }
            return sorted;
        } else if (element.isJsonArray()) {
            JsonArray array = new JsonArray();
            for (JsonElement item : element.getAsJsonArray()) {
                array.add(sortJsonElement(item));
            }
            return array;
        }
        return element;
    }


}
